﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>

// 自定义类型：联合体(共用体)和枚举

// 1.自定义类型：联合体(共用体)

//	联合体和结构体一样，可以又一个或者多个成员组成，成员也可以是不同的类型
//	但是这些成员是共用一块空间

// 1.1 联合体的声明
#if 0
union S
{
	char c;
	int i;
};

int main()
{
	union S s = { 0 };
	s.c = 1;
	printf("s.i = %d\n", s.i); // 1
	printf("sizeof(s) = %zd\n", sizeof(s)); // 4
	return 0;
}
// 由于共用一块空间我们可以看到，我们赋值s.c,但是当我们打印s.i时依然是1。
// 由于共用一块空间我们可以看到, 计算整个联合体类型s时大小为4。
#endif

// 1.2 联合体的特点

//  联合的成员是共⽤同⼀块内存空间的，这样⼀个联合变量的⼤⼩，⾄少是最⼤成员的⼤⼩（因为联合
//  ⾄少得有能⼒保存最⼤的那个成员）。

// 代码1
#if 0
union Un
{
	char c;
	int i;
};

int main()
{
	union Un u = { 0 };
	printf("&(u.c) = %p\n", &(u.c));
	printf("&(u.i) = %p\n", &(u.i));
	printf("&u     = %p\n", &u);
	return 0;
}
// 存储地址一样
#endif

// 代码2
#if 0
union Un
{
	char c;
	int i;
};

int main()
{
	union Un u = { 0 };
	u.i = 0x11223344;
	printf("u.i = %x\n", u.i);
	printf("u.c = %x\n", u.c);
	return 0;
}
// 小端存储，共用空间
#endif

// 1.3 联合体的大小计算
//• 联合的⼤⼩⾄少是最⼤成员的⼤⼩。
//• 当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候，就要对⻬到最⼤对⻬数的整数倍。
#if 0
#include <stdio.h>
union Un1
{               //大小    个数
	char c[10]; // 1   -   10
	int i;      // 4   -   1
}; 
union Un2
{              // 大小     个数
	short c[3];//  2   -   3
	int i;     //  4   -   1
};
int main()
{
	//下⾯输出的结果是什么？
	printf("%zd\n", sizeof(union Un1)); //12
	printf("%zd\n", sizeof(union Un2)); //8
	return 0;
}
#endif

#if 0
//1.4使⽤联合体是可以节省空间的，举例：
//⽐如，我们要搞⼀个活动，要上线⼀个礼品兑换单，礼品兑换单中有三种商品：图书、杯⼦、衬衫。
//每⼀种商品都有：库存量、价格、商品类型和商品类型相关的其他信息。
//图书：书名、作者、⻚数
//杯⼦：设计
//衬衫：设计、可选颜⾊、可选尺⼨
struct gift_list
{
	// 公共属性
	int stock_number; //库存量
	double price; //价格
	int item_type; //商品类型

	union
	{
		struct 
		{
			char title[20]; //书名
			char author[20]; //作者
			int num_pages; //页数
		}book;

		struct
		{
			char design[30];//设计
		}mug;

		struct
		{
			char design[30];//设计
			int colors;//颜色
			int size; //尺寸
		}shirt;

	}item;

};
#endif

#if 0
// 1.5使用联合体写一个程序判断机器是大端还是小端存储
int check()
{
	union U
	{
		char c;
		int i;
	};
	union U u = { 0 };
	u.i = 1;
	return (u.c);
}

int main()
{
	int ret = check();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}
#endif


 /*2.自定义类型：枚举

枚举顾名思义就是⼀⼀列举。
把可能的取值⼀⼀列举。
⽐如我们现实⽣活中：
⼀周的星期⼀到星期⽇是有限的7天，可以⼀⼀列举
性别有：男、⼥、保密，也可以⼀⼀列举
⽉份有12个⽉，也可以⼀⼀列举
三原⾊，也是可以意义列举*/

// 2.1枚举的声明
#if 0
enum Colcor //颜色
{
	RED,
	BLUE,
	GREEN
};
#endif
// 枚举类型 - enum Colcor

// RED,BLUE,GREEN 属于枚举变量

//枚举变量的值默认从0开始，依次递增1，当然在声明枚举类型的时候也可以赋初值。

// 2.2 枚举变量的优点

//枚举的优点：
//1. 增加代码的可读性和可维护性
//2. 和#define定义的标识符⽐较枚举有类型检查，更加严谨。
//3. 便于调试，预处理阶段会删除 #define 定义的符号
//4. 使⽤⽅便，⼀次可以定义多个常量
//5. 枚举常量是遵循作⽤域规则的，枚举声明在函数内，只能在函数内使⽤

//题目：
#if 0
//1.下面代码的结果是:
enum ENUM_A
{
	X1,
	Y1,
	Z1 = 255,
	A1,
	B1,
};
int main()
{
	enum ENUM_A enumA = Y1;
	enum ENUM_A enumB = B1;
	printf("enumA = %d\nenumB = %d\n", enumA, enumB);
	return 0;
}

//因为枚举变量默认从0开始，X1=0，Y1=1,

//但是Z1 = 255,所有后面从255开始数起， A1=256，B1=257。 
#endif


#if 0
// 模拟实现atoi函数

 //1.传入空字符会直接返回0 - 使用函数isspace判断，头文件是<ctype.h>，如果是空字符直接返回0。

 //2.一开始的空格字符会过滤直到遇见数字字符或其他字符，后面再次遇到空格字符直接返回。

 //3.我们输入的带符号的字符也会被转化成数字，如"-123" -> -123， "123" -> 123 。

 //4.判断是数字字符还是其他字符可以使用isdigit函数，头文件是<ctype.h>。

 //5.当我们输入比int类型的最大值还大的数值时返回int类型最大值，同理最小值也是如此。

 //6.将一个字符数字转化成数字可以使用ASCLL码，我们根据ASCLL码表可以知道 '1' - '0' = 1,以此类推我们就可以得到0~9。


int my_atoi(const char* str) // int - 返回类型 ， const char* str -接受要转化的字符，const保障不改变原来的值
{
	assert(str!=NULL); //断言str,不可以为空指针,头文件<assert.h>

	if (*str=='\0') //判断是否为\0，如果是返回0。
	{
		return 0;
	}

	// 判断并且过滤一开始的空格
	while (isspace(*str))
	{
		str++;
	}

	// 判断是否存在符号
	int flge = 1;
	if (*str == '+')
	{
		flge = 1;
		str++;
	}
	else if (*str == '-') // 不可以直接使用else不判断，可能会出现字符不是'-'的情况。
	{
		flge = -1;
		str++;
	}

	// 判断并且转化字符数字
	long long num = 0; //使用long long 类型是为了接受更大的数值
	while (*str != '\0')
	{
		if (isdigit(*str))
		{
			num = num * 10 + (*str - '0') * flge;

			//比int类型的最大值还大的数值时返回int类型最大值，同理最小值也是如此。

			if (num > INT_MAX) 
			{
				num = INT_MAX;
			}

			if (num < INT_MIN)
			{
				num = INT_MIN;
			}
		}
		else
		{
			return (int)num; //如果没有遇到数字字符直接返回，遇到后又中止了也返回中止前转化的数字字符。
		}
		str++;
	}

	return (int)num;
}

int main()
{
	char str[] = "-12a3";
	int ret = my_atoi(str);
	printf("%d\n", ret);
	return 0;
}
#endif

#if 0
//题目名称：
//使用malloc函数模拟开辟一个二维数组
//题目内容：
//使用malloc函数模拟开辟一个3 * 5的整型二维数组，开辟好后，使用二维数组的下标访问形式，访问空间。

int main()
{
	// 开辟函数
	int** p = (int**)calloc(3, 3 * sizeof(int*));
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		p[i] = (int*)calloc(5, 5 * sizeof(int));
	}
	int j = 0;
	/*for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			p[i][j] = 0;
		}
	}*/
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", p[i][j]);
		}
		printf("\n");
	}
	for (i = 0; i < 3; i++)
	{
		free(p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}
#endif


#if 0
//题目名称：
//写一个程序拷贝文件
//题目内容：
//使用所学文件操作，在当前目录下放一个文件data.txt，写一个程序，将data.txt文件拷贝一份，生成data_copy.txt文件。
//
//基本思路：
//
//打开文件data.txt，读取数据
//打开文件data_copy.txt，写数据
//从data.txt中读取数据存放到data_copy.txt文件中，直到文件结束。
//关闭两个文件

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	FILE* pw = fopen("data_copy.txt", "w");
	if (NULL == pw)
	{
		perror("fopen for write");
		fclose(pf);
		return 1;
	}

	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		fputc(ch, pw);
	}

	fclose(pf);
	fclose(pw);
	pf = NULL;
	pw = NULL;
}
#endif